package com.neveu.android.ds.socketmessenger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

import android.util.Log;

public class NetServent {

	Socket clientSock;
	ServerSocket serverSock;
	private String loopbackIP;
	private int connectPort;
	private PrintWriter pw;
	private MessageListener msgListener;
	private BufferedReader in;

	
	public NetServent(String loopbackIP, final int listenPort, int clientPort)
	{
		this.loopbackIP = loopbackIP;
		this.connectPort = clientPort;
		

		new Thread(new Runnable() {
			public void run()
			{
				try {
					serverSock = new ServerSocket(listenPort);
				}catch(IOException e){
					Log.e("NetServent: Init ServerSocket","Could not establish listening socket on port " + listenPort);
				}
				
				while(!serverSock.isClosed())
				{
					try {
						clientSock = serverSock.accept();
						pw = new PrintWriter(clientSock.getOutputStream(), true);
						readInput();
					}catch(IOException e) {
						/* this is caught whether there is a legitimate socket issue, or if serverSock
						 * has been explicitly closed. Either way, readInput() is not reached, which is what we want.
						 */
						Log.d("NetServent: ServerSocket.accept()", e.getMessage());
					}
				}
			}}).start();
	}
	
	private class ConnectThread implements Runnable
	{
		public void run()
		{
			try{
				clientSock = new Socket(loopbackIP, connectPort);
				pw = new PrintWriter(clientSock.getOutputStream(), true);
				readInput();
				
			} catch (UnknownHostException e) {
	            System.err.println("Don't know about host: " + loopbackIP + ".");
	        } catch (IOException e) {
	            System.err.println(e.getMessage());
	        }
			
		}
		
	}
	
	
	public void connect(int port)
	{
		connectPort = port;
		if(clientSock != null && clientSock.isConnected())
		{
			reset();
		}
		
// *** Use these conditions for persistent connection. ***
//		if(clientSock == null || !clientSock.isConnected())
//		{
			new Thread(new ConnectThread()).start();
//		}
		
	}
	
	public void sendMessage(String msg)
	{
		if(clientSock == null || !clientSock.isConnected())
			return;
				
		pw.println(msg);
	}
	
	public synchronized void readInput()
	{
		String inStr = "";
		
		try{
			in = new BufferedReader(new InputStreamReader(clientSock.getInputStream()));

			/* How to nicely interrupt this blocking call when we wish to exit??
			 *  */
			while((inStr = in.readLine()) != null)
			{
				
				if(msgListener != null)
					msgListener.receiveMessage(inStr + "\n");
				else
					Log.e("readInput", "MessageListener not initialized");
			}
			
			in.close();

		}catch (IOException e) {
			Log.e("readInput", e.getMessage());
		}
		finally{
		}
	}
		
	private void reset()
	{
		try{
			pw.close();
			clientSock.shutdownInput();
			clientSock.close();		
		}catch(Exception e)
		{
			Log.d("NetServent.reset", e.getMessage());
		}
	}
	
	public void disconnect()
	{
		reset();
		try{
			serverSock.close();
		}catch(IOException e)
		{
			Log.d("NetServent.disconnect", e.getMessage());
		}		
	}
	
	public void setMessageListener(MessageListener ml)
	{
		msgListener = ml;
	}
}
